﻿#light

/// <summary>
/// Declares an interface for a type of product object.
/// </summary>
type AbstractProductA =
    interface
    end

/// <summary>
/// Declares an interface for a type of product object.
/// </summary>
type AbstractProductB =
    abstract Interact : a: AbstractProductA -> unit

/// <summary>
/// Defines a product object to be created by the corresponding
/// concrete factory.
/// Implements the AbstractProduct interface.
/// </summary>
type ProductA1() =
    interface AbstractProductA

/// <summary>
/// Defines a product object to be created by the corresponding
/// concrete factory.
/// Implements the AbstractProduct interface.
/// </summary>
type ProductB1() =
    interface AbstractProductB with
        member this.Interact(a: AbstractProductA) =
            printfn "%s interacts with %s" (this.GetType().Name) (a.GetType().Name)

/// <summary>
/// Defines a product object to be created by the corresponding
/// concrete factory.
/// Implements the AbstractProduct interface.
/// </summary>
type ProductA2() =
    interface AbstractProductA

/// <summary>
/// Defines a product object to be created by the corresponding
/// concrete factory.
/// Implements the AbstractProduct interface.
/// </summary>
type ProductB2() =
    interface AbstractProductB with
        member this.Interact(a: AbstractProductA) =
            printfn "%s interacts with %s" (this.GetType().Name) (a.GetType().Name)

/// <summary>
/// Declares an interface for operations that create
/// abstract product.
/// </summary>
type AbstractFactory =
    abstract CreateProductA : unit -> AbstractProductA
    abstract CreateProductB : unit -> AbstractProductB

/// <summary>
/// Implements the operations to create concrete product objects.
/// </summary>
type ConcreteFactory1() =
    interface AbstractFactory with
        member this.CreateProductA() =
            ProductA1() :> AbstractProductA

        member this.CreateProductB() =
            ProductB1() :> AbstractProductB

/// <summary>
/// Implements the operations to create concrete product objects.
/// </summary>
type ConcreteFactory2() =
    interface AbstractFactory with
        member this.CreateProductA() =
            ProductA2() :> AbstractProductA

        member this.CreateProductB() =
            ProductB2() :> AbstractProductB

/// <summary>
/// Uses interfaces declared by AbstractFactory and
/// AbstractProduct classes.
/// </summary>
type Client(factory: AbstractFactory) =
    let prodA = factory.CreateProductA()
    let prodB = factory.CreateProductB()

    member this.Run() =
        prodB.Interact(prodA)
